VDO Hub
Unlock videos by watching ads
Explore
Loading...
Ad Slot
Watch Ad to Unlock
Open in Bot
`; if(typeof obj.src === 'string') return ``; return `
Unsupported ad JSON
`; } }catch(e){ // fallthrough to raw } } return s; } function renderAd(code, title){ qs('ad-title').innerText = title || 'Ad Slot'; const slot = qs('ad-slot'); slot.innerHTML = ''; const ifr = document.createElement('iframe'); ifr.setAttribute('sandbox', 'allow-scripts allow-forms allow-popups allow-modals allow-presentation allow-top-navigation-by-user-activation'); ifr.srcdoc = `
${code || '
Ad Slot
'} `; slot.appendChild(ifr); } function playAdAndWait(minMs=3000){ return new Promise(res=>{ const start = Date.now(); const t = setInterval(()=>{ if(Date.now()-start >= minMs){ clearInterval(t); res(); } }, 200); }); } async function sendStat(s){ try{ await fetch(`${API}/api/stats`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ videoId: vid, step: step+1, status: s }) }); }catch(_){} } function unlockUI(){ const img = qs('v-thumb'); img.style.filter = 'blur(0)'; img.style.transform = 'scale(1.00)'; showStatus('✅ Video unlocked!', 'ok'); const bot = activeData.botUsername || 'vdohubbot'; const link = `https://t.me/${bot}?start=video_${vid}`; const native = `tg://resolve?domain=${bot}&start=video_${vid}`; const a = qs('bot-btn'); a.href = tg ? native : link; a.innerText = 'Get video in Bot'; show(a); const btn = qs('unlock-btn'); btn.disabled = true; btn.innerText = 'Unlocked'; } async function handleUnlock(){ const btn = qs('unlock-btn'); btn.disabled = true; if(step === 0){ renderAd(normalizeAd(activeData.adminAdCode), 'Ad 1 of 2'); showStatus('Ad 1 loading...'); await playAdAndWait(3000); await sendStat('seen'); showStatus('Ad 1 done ✅', 'ok'); step = 1; btn.innerText = (activeData.userAdCode && activeData.userAdCode.trim()) ? 'Watch Ad 2' : 'Unlock'; btn.disabled = false; return; } if(step === 1){ if(activeData.userAdCode && activeData.userAdCode.trim()){ renderAd(normalizeAd(activeData.userAdCode), 'Ad 2 of 2'); showStatus('Ad 2 loading...'); await playAdAndWait(3000); await sendStat('seen'); showStatus('Ad 2 done ✅', 'ok'); }else{ showStatus('Skipping Ad 2 (not configured)'); } step = 2; await fetch(`${API}/api/stats`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ videoId: vid, step: 2, status: 'complete' }) }).catch(()=>{}); unlockUI(); return; } } async function init(){ vid = resolveVid(); if(vid){ swapPage(true); const r = await fetch(`${API}/api/video/${vid}`); if(!r.ok){ showStatus('❌ Video not found','err'); return; } activeData = await r.json(); qs('v-title').innerText = activeData.title || 'Untitled'; qs('v-thumb').src = activeData.thumbnail || ''; const hasUserAd = !!(activeData.userAdCode && activeData.userAdCode.trim()); qs('unlock-btn').innerText = hasUserAd ? 'Watch Ad to Unlock' : 'Watch Ad (1 step)'; qs('unlock-btn').addEventListener('click', handleUnlock); }else{ swapPage(false); const r = await fetch(`${API}/api/videos`); const list = r.ok ? await r.json() : []; const feed = qs('feed'); feed.innerHTML = ''; list.forEach(v=>{ const a = document.createElement('a'); a.href = `?v=${v.video_id}`; a.className = 'card'; a.innerHTML = `
${escapeHtml(v.title || 'Untitled')}
`; feed.appendChild(a); }); } } function escapeHtml(s){ return (s||'').replace(/[&<>"']/g,m=>({ '&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); } init().catch(e=>{ console.error(e); showStatus('Error loading','err'); });